<html><head><title>Chapter 14 Timing Framework: Fundamentals - Core Concepts</title></head><body><div id='tit'>Chapter 14 Timing Framework: Fundamentals - Core Concepts</div><div id='cate'>源码品读</div><div id='date'>2010年06月22日 星期二 03:01 P.M.</div><div id='page'>13</div><a id='url' href='http://hi.baidu.com/hxzon/blog/item/b921411079f189f1c2ce79ff.html'>http://hi.baidu.com/hxzon/blog/item/b921411079f189f1c2ce79ff.html</a><div id='cnt'><div> 
 <p>Chapter 14 Timing Framework: Fundamentals - Core Concepts</p> 
 <p>《Filthy Rich Clients: Developing Animated and Graphical Effects for Desktop Java? Applications》pdf</p> 
 <p>Timing Framework Animator 设计方案 hxzon</p> 
 <p>------------------------------------------------</p> 
 <h2>Core Concepts</h2> 
 <p>Several key concepts and properties are embedded in the central classes used by<br /> the Timing Framework:</p> 
 <p>•Animator : This class encapsulates most of the functionality discussed in<br /> this chapter, but it is worth discussing it separately so that we can see how<br /> an Animator, and the animation it deﬁnes, is created and run.</p> 
 <p>• Callbacks: An application must have a means to be notiﬁed of events dur-<br /> ing the animation. In this way, an application can be involved in the ani-<br /> mation to perform appropriate actions based on the state of the animation.<br /> Event notiﬁcation is handled through callbacks to an interface that appli-<br /> cation code may implement. This mechanism is similar to what we saw<br /> earlier in our discussion of the built-in Java timers, except that the Timing<br /> Framework has more callbacks with more information to enable more ﬂex-<br /> ibility in your animations.</p> 
 <p>• Duration: The duration value deﬁnes the length of time that the animation<br /> will last. An animation stops automatically when this duration has elapsed.<br /> You may also specify that an animation should run indeﬁnitely.</p> 
 <p>• Repetition: Some animations are intended to run once and then ﬁnish. Oth-<br /> ers may run indeﬁnitely. Still others may run with a ﬁnite duration and then<br /> repeat when they are done.</p> 
 <p>• Resolution: The resolution of an animation controls the frame rate of the<br /> animation. This concept was discussed at length in Chapter 12, “Anima-<br /> tion Fundamentals.”</p> 
 <p>• Starting behavior: An animation may not want to start with the default<br /> behavior of moving forward from the beginning. It may instead want to run<br /> backwards or start from some other point than the beginning. It may also<br /> want to delay for some time before starting.</p> 
 <p>• Ending behavior: By default, an animation holds its ﬁnal value when it is<br /> stopped. You might choose, instead, to have an animation reset to the start<br /> state when ﬁnished.</p> 
 <p>• Interpolation: The easiest kind of interpolation is linear interpolation,<br /> which we discussed in earlier chapters. But there are other kinds of inter-<br /> polation that we can apply to give the animation nonlinear behavior.</p> 
 <h2>Animator</h2> 
 <p>Animator is the core class of the entire framework. Users of the Timing<br /> Framework create Animators with information that details the animations they<br /> want to run. The properties that deﬁne an animation are set through a combina-<br /> tion of the constructors, which enable setting the most common properties, and<br /> other methods in the class. Animations are started and stopped by calling<br /> methods in this class. Finally, this class is responsible for issuing ongoing ani-<br /> mation events while the animation is running, which is discussed in the next<br /> section, “Callbacks.”</p> 
 <p>Creation</p> 
 <p>Animators are created through one of the three constructor methods:<br /> Animator(int duration)<br /> This method takes only a duration parameter, discussed later, which controls how<br /> long the animation will run. Note that this constructor takes no TimingTarget<br /> parameter. Callers would typically add one or more TimingTarget objects<br /> later via the addTarget() method; otherwise the Animator will run but will<br /> issue no events.<br /> 2<br /> Animator(int duration, TimingTarget target)<br /> This variant, the most common, takes a duration and a TimingTarget. The<br /> TimingTarget, discussed in the section “Callbacks,” contains the methods<br /> that will be called with animation events as the animation runs.<br /> Animator(int duration, double repeatCount, <br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Animator.RepeatBehavior repeatBehavior,<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TimingTarget target)<br /> This ﬁnal variant takes the same duration and target parameters as before, but<br /> also takes two other parameters that control how the animation is repeated<br /> over time, as discussed in the section “Repetition.”</p> 
 <h2>Control Flow</h2> 
 <p>There are several methods that control the running and stopping of the anima-<br /> tion. Note that, as discussed in the section “Duration,” animations may stop<br /> automatically. But animations may also be programmatically halted by some of<br /> the methods of Animator described here.</p> 
 <p>void start()<br /> This method starts the animation, which results in callbacks to the TimingTarget<br /> begin() and timingEvent() methods, as described in the section “Callbacks.”</p> 
 <p>void stop()<br /> This method stops the animation, which results in a call to the end() method<br /> of TimingTarget, notifying any targets that the animation has completed.</p> 
 <p>void cancel()<br /> This method is like stop() except that TimingTarget.end() will not be<br /> called for any targets. It’s like pulling the plug on the animation instead of let-<br /> ting it stop normally.</p> 
 <p>void pause()<br /> This method pauses a running animation, which stops the animation in its cur-<br /> rent state until and unless a later call to resume() is issued.</p> 
 <p>void resume()<br /> This method resumes an animation that has been paused. The animation will<br /> continue from its previously paused state, as if no time had passed between<br /> pause() and resume(). This method has no effect on an animation that has<br /> not been paused.</p> 
 <p>boolean isRunning()<br /> This method queries whether the animation is currently running.</p> 
 <h2>Controlling a Running Animation</h2> 
 <p>It is worth noting that most of the parameters that control an animation, such as<br /> the duration and repetition parameters, make sense only on a non-running ani-<br /> mation. Once an animation is running, it is not clear how changes to these<br /> parameters should be interpreted. Therefore, most of the methods of Animator<br /> that set these parameters, except where noted, will throw an exception if called<br /> while an animation is running.</p> 
 <h2>Callbacks</h2> 
 <p>TimingTarget</p> 
 <p>Animations in the Timing Framework run by having the animation code in<br /> Animator call back into one or more TimingTarget implementations. A<br /> TimingTarget object exists to receive timing events from an Animator. The<br /> TimingTarget object is the connection between the animation running through<br /> Animator and the animation actually doing something. The callback methods in<br /> TimingTarget are given information about the current animation state and can<br /> set up object state, calculate new property values, or do anything else appropriate<br /> for the situation.<br /> When you set up an Animator, you give it one or more TimingTarget objects<br /> through one of the constructors for Animator and through the addTarget()<br /> method of Animator. As the animation runs, the Animator object calls the meth-<br /> ods in each of its TimingTarget objects.<br /> The TimingTarget interface has four different event methods to implement:<br /> public interface TimingTarget {<br /> public void begin();<br /> public void end();<br /> public void repeat();<br /> public void timingEvent(float fraction);<br /> }</p> 
 <p>void begin()<br /> This method is called by Animator when the animation is ﬁrst started. It<br /> allows the timing target to perform any necessary setup prior to running the<br /> animation.</p> 
 <p>void end()<br /> This method is called when the animation is ﬁnished, either because the anima-<br /> tion completed naturally by running for the speciﬁed duration and number of<br /> repetitions or because the stop() method was called on this target’s Animator.<br /> This method allows the target to perform any necessary cleanup operations.<br /> The end() method can be used as a mechanism to help sequence animations<br /> together. For example, a target can use the end() call to signal that some other<br /> dependent animation should start. Note, however, that triggers provide an even<br /> easier mechanism for this functionality, as we see in Chapter 15.</p> 
 <p>void repeat()<br /> This method is called during a repeating animation, every time the animation<br /> begins another repetition. Repeating animations are discussed later.</p> 
 <p>void timingEvent(float fraction)<br /> This method is the most important method in this interface and, in fact, in the<br /> entire framework.<br /> 3 timingEvent() provides the target with the fraction, from<br /> 0 to 1, of the animation that has elapsed. The target can then use this informa-<br /> tion to change whatever properties need to be changed during the animation<br /> and to schedule a repaint if necessary.<br /> The fraction value is directly related to the duration property. If Animator is<br /> given a duration of 2 seconds, then an animation that issues a timingEvent()<br /> one second after starting would call timingEvent() with a fraction value<br /> of .5.<br /> 4<br /> The fraction is a useful value to have. If you want to animate some variable<br /> linearly between start and end values, it is important to know what fraction of<br /> the animation has elapsed. If the animation is halfway through, then you know<br /> to set your variable to halfway between its start and end values.<br /> Some of the parameters of Animator, such as the start direction and the reversing<br /> behavior, may make an animation run backwards. When this happens, the frac-<br /> tion values received in timingEvent() run in reverse, too. That is, the fraction<br /> always represents the elapsed fraction of the animation from the start to the end.<br /> An animation running in reverse starts at the end point and runs in reverse. So,<br /> for example, an animation that starts at the end and runs in reverse will issue val-<br /> ues from 1 down to 0.</p> 
 <p>TimingTargetAdapter</p> 
 <p>The TimingTargetAdapter class is a simple implementation of TimingTarget,<br /> providing empty methods for that interface. This class is provided as a utility for<br /> subclasses that want to receive only speciﬁc Animator events and do not want to<br /> implement all of the TimingTarget methods just to get the one or two that they<br /> really care about.</p> 
 <h2>Duration</h2> 
 <p>The discussion of timingEvent() relates directly to the duration property,<br /> because the fraction elapsed of an animation is calculated from the time elapsed<br /> so far and the total duration of that animation. The duration is speciﬁed in either<br /> one of Animator’s constructors, as seen previously, or in the following method:<br /> setDuration(int duration)<br /> Both the setDuration() method and Animator’sconstructors set the duration<br /> for the animation in milliseconds. For example, an animation is assigned a dura-<br /> tion of 2 seconds through a constructor, like this:<br /> Animator myAnimation = new Animator(2000);<br /> or through a later assignment to an existing animation:<br /> myAnimation.setDuration(2000);<br /> There is one additional, important value that a duration may have:<br /> Animator.INFINITE, which tells Animator that this animation should run indef-<br /> initely. Note that such animations will still call timingEvent() on a regular<br /> basis, but the fraction value in that call will be meaningless because there can be<br /> no elapsed percentage of an inﬁnite amount of time.<br /> 5<br /> An important concept to note in relation to duration is that all animations are<br /> tracked in Animator in fractional time. That is, an animation, regardless of<br /> actual duration, may be thought of in terms of the percentage that the animation<br /> has elapsed at any time. So any animation, other than one of INFINITE duration,<br /> has a fractional duration of exactly 1. Calls to timingEvent() during the anima-<br /> tion will use a fractional value instead of an actual duration. This mechanism<br /> tends to be easier to deal with for callers, which get more useful information<br /> from knowing that an animation is one-quarter elapsed than that it is 500 ms into<br /> whatever its total duration may be. The concept of the elapsed fraction comes up<br /> often. We typically discuss animations in terms of this fraction instead of the<br /> total duration simply because that is what Animator keeps track of and reports to<br /> its targets, and because it is much more powerful and useful to Animator’s users.</p> 
 <h2>Repetition</h2> 
 <p>A repeated animation is a common pattern. Repetition can take the form of run-<br /> ning the same animation over and over, like an indeﬁnite progress bar whose sta-<br /> tus always crawls from the left to the right. A repeating animation can also be<br /> constantly reversing, like a pulsating button that has a glow effect in which the<br /> glow is constantly glowing toward full intensity and then dimming back down to<br /> some default state. Instead of constructing separate animations for each repeti-<br /> tion or creating one large animation that handles all of the repetitions as an<br /> implementation detail, the framework provides the ability to deﬁne the core ani-<br /> mation and then parameters for how that animation should be repeated.<br /> There are two properties of Animator that control repetition: the number of<br /> times the animation should be repeated and the behavior upon each repetition.<br /> These properties are controlled through the following constructor and methods:<br /> Animator(int duration, double repeatCount,<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Animator.RepeatBehavior repeatBehavior, <br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TimingTarget target)<br /> void setRepeatCount(double repeatCount)<br /> void setRepeatBehavior(Animator.RepeatBehavior repeatBehavior)<br /> In this constructor and these methods, the repetition behavior is controlled<br /> through the repeatCount and repeatBehavior variables. repeatCount is sim-<br /> ply the number of times that the animation should be repeated. This value can be<br /> fractional, such as 2.5, to indicate that the animation may stop partway through.<br /> repeatCount can also, like the duration value, take the value Animator.INFINITE,<br /> which indicates that the animation should repeat indeﬁnitely. <br /> repeatBehavior can have a value of either RepeatBehavior.LOOP or<br /> RepeatBehavior.REVERSE. LOOP repeats the animation in the same direction<br /> every time. When the animation reaches the end, it starts over from the beginning.<br /> So, for example, the animation fraction being passed into timingEvent() calls<br /> will go from 0 to 1, then 0 to 1, and so on, until repeatCount is reached or the ani-<br /> mation is otherwise stopped. REVERSE creates an animation that reverses direction<br /> whenever it reaches the end of an animation. For example, the animation fraction<br /> passed into timingEvent() calls will go from 0 to 1, 1 to 0, 0 to 1, and so on.</p> 
 <h2>Resolution</h2> 
 <p>The resolution of Animator controls the amount of time between each call to<br /> timingEvent(). The default used by Animator is reasonable for most situations,<br /> so developers should not need to change the value in general, but changing it is a<br /> simple matter of calling setResolution():<br /> void setResolution(int resolution)<br /> This method sets the number of milliseconds between each call to timingEvent().<br /> Recall from our discussion of resolution in Chapter 12, “Animation Fundamen-<br /> tals,” that the actual resolution may be dependent on such factors as the internal<br /> timing mechanism being used and the runtime platform. The Timing Framework<br /> currently uses the Swing timer internally, and its resolution is thus constrained to<br /> the resolution of that timer for now.<br /> 6</p> 
 <h2>Start Behavior</h2> 
 <p>There are three factors about the starting state of the animation that you can con-<br /> trol: the start delay, the direction, and the initial fraction.</p> 
 <p>Start Delay<br /> Some animations may wish to have an initial delay before commencing. The<br /> amount of this delay is controlled through the setStartDelay() method:<br /> void setStartDelay(int startDelay)<br /> where the startDelay value is in terms of milliseconds.</p> 
 <p>Start Direction<br /> By default, an animation runs forward when it starts. The initial direction can be<br /> changed to run the animation in reverse instead. This setting is controlled<br /> through the setStartDirection() method:<br /> void setStartDirection(Animator.Direction startDirection)<br /> where startDirection can have the value of either Direction.FORWARD, which<br /> is the default behavior, or Direction.BACKWARD.</p> 
 <p>Start Fraction<br /> By default, an animation begins at fraction 0. This setting can be changed to start<br /> from any point during the animation by calling setStartFraction():<br /> void setStartFraction(float startFraction)<br /> where startFraction is a value from 0 to 1, representing the fraction elapsed of<br /> the animation. Note that to run an animation from the end to the beginning, the<br /> caller should set the initial fraction to 1 and the direction to BACKWARD. An exam-<br /> ple of this behavior is shown in the FadingButtonTF demo later.</p> 
 <h2>End Behavior</h2> 
 <p>By default, an animation will hold its ﬁnal value when it ﬁnishes. For example,<br /> an animation that ﬁnishes a normal forward cycle from 0 to 1 will hold the value<br /> 1 at the end. This can be changed to reset to 0 at the end instead by calling<br /> setEndBehavior():<br /> void setEndBehavior(Animator.EndBehavior endBehavior)<br /> where endBehavior can have the value of either EndBehavior.HOLD, which is the<br /> default behavior, or EndBehavior.RESET, which sends out a ﬁnal timingEvent()<br /> with a fraction of 0 at the end of the animation.</p> 
 <h2>Demo: FadingButton Reprise</h2> 
 <p>We’re not quite done with the core Animator features. We still need to cover the<br /> important area of Interpolator. But it’s time for a break to see some of the con-<br /> cepts in action.<br /> Let’s look at what we can do with just the classes that we have covered so far.<br /> We have many classes in the framework yet to cover, but the power and ﬂexibil-<br /> ity of just the basic Animator and TimingTarget classes provides enough to<br /> enable simple code that drives powerful animations. In particular, think of the<br /> things that we had to do with the built-in timers to animate our GUIs in previous<br /> chapters or the things that seemed unapproachably tedious, like cyclic, repeating<br /> animations.<br /> For a simple example, let’s revisit the FadingButton demo that we discussed<br /> Chapter 12. While the application is not terribly complex, it is a useful example<br /> for showing how using Animator helps make animations easier to program.<br /> Recall in that example that we deﬁned a custom JButton subclass with various<br /> methods for rendering the button translucently and animating the value of alpha.<br /> First, there were some instance variables to help track state:<br /> float alpha = 1.0f;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // current opacity of button<br /> Timer timer;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // for later start/stop actions<br /> int animationDuration = 2000; // animation will take 2 seconds<br /> long animationStartTime;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // start time for each animation<br /> In the constructor, we created the Timer object that ran the animation:<br /> timer = new Timer(30, this);<br /> Finally, we had an actionPerformed() method that served two purposes: It<br /> caught clicks on the button and used them to start and stop the animation, and it<br /> also received Timer events and animated the value of alpha with the following<br /> code:<br /> public void actionPerformed(ActionEvent ae) {<br /> // ... code to handle button clicks not shown here ...<br /> long currentTime = System.nanoTime() / 1000000;<br /> long totalTime = currentTime - animationStartTime;<br /> if (totalTime &gt; animationDuration) {<br /> &nbsp;&nbsp;&nbsp; animationStartTime = currentTime;<br /> }<br /> float fraction = (float)totalTime / animationDuration;<br /> fraction = Math.min(1.0f, fraction);<br /> // This calculation will cause alpha to go from 1 to 0 <br /> // and back to 1 as the fraction goes from 0 to 1<br /> alpha = Math.abs(1 - (2 * fraction));<br /> repaint();<br /> }</p> 
 <p>Now that we have the power of Animator, let’s see how the code changes. You<br /> can see and run the code for this version, called FadingButtonTF, on the book’s<br /> Web site. First of all, we need fewer instance variables:<br /> float alpha = 1.0f;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // current opacity of button<br /> Animator animator;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // for later start/stop actions<br /> int animationDuration = 2000; // each cycle takes 2 seconds<br /> We do not need to track the animationStartTime because we no longer need to<br /> calculate the fraction of the cycle elapsed. Animator does this for us.<br /> The constructor is similar to what it was before, although the declaration for<br /> Animator is a bit different from that of Timer:<br /> animator = new Animator(animationDuration/2, Animator.INFINITE,<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; RepeatBehavior.REVERSE, this); <br /> There are a few interesting bits about this call. First of all, we are using a dura-<br /> tion of only half of animationDuration. This difference is because of how this<br /> new animation will be handled. Previously, each individual animation would<br /> consist of both the fade-out and fade-in portions, which we wanted to last for 2<br /> seconds. With Animator, we can declare a more interesting reversing animation<br /> that reverses every second, which gives us the same result. Also, we see that we<br /> are going to be repeating inﬁnitely, which is the same behavior as in the earlier<br /> Timer example. We declare a REVERSE behavior so that the animation reverses<br /> direction every time it repeats. And ﬁnally, we pass in this as the TimingTarget<br /> that will be called with timing events. Our object implements the TimingTarget<br /> interface in order to catch timingEvent() calls, just as the previous version of<br /> the demo received events from Timer in its actionPerformed() method.<br /> Additionally, since we want to start at an opaque value and animate toward<br /> transparency, we need to make sure that we link up the animation fraction and<br /> our alpha value correctly. Both values vary between 0 and 1 over the course of<br /> the animation, so we’re almost there. But since our animation starts at 0 by<br /> default and we want our alpha value to start at 1, or fully opaque, these values<br /> are going to run opposite of each other. We can either have alpha represent the<br /> inverse of the fraction, so that alpha would be 1 when fraction was at 0, or we<br /> can use additional facilities in Animator to start the animation at the end, play-<br /> ing backwards. This will ensure that the animation fraction starts at the same<br /> value as we want for our alpha. We add the following code to set the Animator<br /> properties accordingly:<br /> animator.setStartFraction(1.0f);<br /> animator.setStartDirection(Direction.BACKWARD);<br /> Finally, let’s see the actual animation code for this new version of the demo. This<br /> time, the code is in the timingEvent() method, which is the target for Animator’s<br /> timing events, instead of the old actionPerformed() method. Compare the<br /> code in actionPerformed() to this approach for timingEvent():<br /> public void timingEvent(float fraction) {<br /> alpha = fraction;<br /> repaint();<br /> }<br /> Note that we do not need to calculate the fraction elapsed of the animation,<br /> because it is given to us. Also, the ﬂexibility in how we deﬁned the animation,<br /> starting at the end and running backwards in the ﬁrst animation, simpliﬁed our<br /> alpha calculation to simply equal the fraction itself. A screenshot of the applica-<br /> tion is seen in Figure 14-1.<br /> Figure 14-1&nbsp;&nbsp; FadingButtonTF: same as the FadingButton demo, but with less code.<br /> This new version of the demo behaves exactly like the old one, but with less<br /> code. The demo is, by design, very simple, so it does not really show off the<br /> power of the Timing Framework as much as the simpler code that is possible,<br /> even for very easy animations. But it would help for us to develop a more inter-<br /> esting demo that shows off more about the framework. We develop this demo,<br /> The Racetrack Demo, throughout this chapter and the next one so that you can<br /> see how the different elements of the framework work together to create more<br /> interesting and complex animations.</p> 
 <p> </p> 
 <p> </p> 
 <p> </p> 
 <p> </p> 
 <p> </p> 
 <p> </p> 
</div></div></body></html>